package co.fitstart.mobile.web.controller.course;

import java.util.Date;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.demo2do.core.BusinessException;
import com.demo2do.core.support.Result;
import com.demo2do.core.utils.DateFormatUtils;
import com.demo2do.core.utils.DateUtils;
import com.demo2do.core.utils.JsonUtils;
import com.demo2do.core.wechat.web.interceptor.WechatJsConfig;

import co.fitstart.entity.coach.CoachRestCalendar;
import co.fitstart.entity.course.round.CourseRound;
import co.fitstart.entity.course.schedule.CourseSchedule;
import co.fitstart.entity.principal.Principal;
import co.fitstart.entity.site.RoomType;
import co.fitstart.entity.site.Site;
import co.fitstart.entity.user.Student;
import co.fitstart.entity.user.User;
import co.fitstart.mobile.cache.accessor.PersistentAccessor;
import co.fitstart.mobile.entity.appointment.Appointment;
import co.fitstart.mobile.entity.appointment.AppointmentPrepare;
import co.fitstart.mobile.entity.appointment.CoachPrepare;
import co.fitstart.mobile.entity.appointment.RoomPrepare;
import co.fitstart.mobile.service.AppointmentService;
import co.fitstart.mobile.service.CoachRestCalendarService;
import co.fitstart.mobile.service.CourseRoundService;
import co.fitstart.mobile.service.CourseScheduleService;
import co.fitstart.mobile.service.PrincipalService;
import co.fitstart.mobile.service.UserService;
import co.fitstart.mobile.web.interceptor.MobileDetect;
import co.fitstart.mobile.web.interceptor.Router;
import co.fitstart.mobile.web.interceptor.WechatAuthority;
import co.fitstart.mobile.web.resolver.Secure;

/**
 * 
 * @author mohanlan
 *
 */
@Controller
@RequestMapping("/profile")
public class CourseAppointController {
    
    private static final Logger logger = LoggerFactory.getLogger(CourseAppointController.class);

    @Autowired
    private PersistentAccessor persistentAccessor;

    @Autowired
    private UserService userService;
    
    @Autowired
    private CourseRoundService courseRoundService;

    @Autowired
    private AppointmentService appointmentService;
    
    @Autowired
    private PrincipalService principalService;
    
    @Autowired
    private CourseScheduleService courseScheduleService;
    
    @Autowired
    private CoachRestCalendarService coachRestCalendarService;
    
    /**
     * View student appoint pages
     * 
     * @param user
     * @param roundId
     * @param siteId
     * @param scheduleId
     * @param day
     * @return
     */
    @SuppressWarnings("deprecation")
    @RequestMapping(value = "/courses/{roundId}/appointment", method = RequestMethod.GET)
    @Router
    @WechatAuthority
    @MobileDetect
    @WechatJsConfig
    public ModelAndView appointment(@Secure User user, 
                                    @PathVariable("roundId") Long roundId,
                                    @RequestParam(name = "site") Long siteId,
                                    @RequestParam(name = "scheduleId", required = false) Long scheduleId,
                                    @RequestParam(name = "day", required = false) @DateTimeFormat(pattern= "yyyy-MM-dd") Date day) {
        
        ModelAndView modelAndView = new ModelAndView();
        
        // 1. load course round and student
        CourseRound courseRound = courseRoundService.loadCourseRound(roundId);
        Student student = userService.getStudent(user);
        
        if(scheduleId != null) {
            CourseSchedule courseSchedule = courseScheduleService.loadCourseSchedule(scheduleId);
            day = day == null ? courseSchedule.getOccurDate() : day;
            modelAndView.addObject("selectedSchedule", courseSchedule);
        }
        
        try {
            
            // 2. validate basic fields
            appointmentService.validate(student, courseRound);
            
            modelAndView.setViewName("course/" + courseRound.getCourseType().getAlias() + "/" + courseRound.getCourseType().getAlias() + "-appointment-view");
            
            // 3. prepare public appointment prepare
            Site site = persistentAccessor.site(siteId);
            AppointmentPrepare appointmentPrepare = appointmentService.prepare(courseRound, site, day);
            modelAndView.addAllObjects(appointmentPrepare.describePrepare());
            
            modelAndView.addObject("currentMinute", new Date().getMinutes());
            
            // prepare courseround site selector
            if(courseRound.getCourseType().isIndividual()) {
                modelAndView.addObject("siteSelect", JsonUtils.toJsonString(persistentAccessor.getSites(courseRound.getSite())));
            }
            
            return modelAndView;
            
        } catch(BusinessException e) {
            
            logger.error("#appointment() error", e);
            
            modelAndView.setViewName("course/course-appointment-error");
            modelAndView.addObject("errorMessage", e.getMessage());
            
            return modelAndView;
            
        } catch(Exception e) {
            
            logger.error("#appointment() error", e);
            
            modelAndView.setViewName("course/course-appointment-error");
            modelAndView.addObject("errorMessage", "");
            
            return modelAndView;
            
        } 
        
    }
    
    /**
     * @param user
     * @param roundId
     * @param siteId
     * @param coachId
     * @param scheduleId
     * @param day
     * @return
     */
    @SuppressWarnings("deprecation")
    @RequestMapping(value = "/courses/{roundId}/coaches/{coachId}/appointment", method = RequestMethod.GET)
    @Router
    @WechatAuthority
    @MobileDetect
    @WechatJsConfig
    public ModelAndView appointmentByCoach(@Secure User user, 
                                           @PathVariable("roundId") Long roundId,
                                           @PathVariable("coachId") Long coachId,
                                           @RequestParam(name = "site") Long siteId,
                                           @RequestParam(name = "scheduleId", required = false) Long scheduleId,
                                           @RequestParam(name = "day", required = false) @DateTimeFormat(pattern= "yyyy-MM-dd") Date day) {
        
        ModelAndView modelAndView = new ModelAndView("course/coach-appointment/coach-appointment-view");
        
        // 1. load course round and student 
        CourseRound courseRound = courseRoundService.loadCourseRound(roundId);
        Student student = userService.getStudent(user);
        
        if(day == null) {
            day = DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd"), "yyyy-MM-dd");
        }
        
        try {
            
            // 2. validate basic fields
            appointmentService.validate(student, courseRound);
            
            // 3. prepare public appointment prepare
            Site site = persistentAccessor.site(siteId);
            AppointmentPrepare appointmentPrepare = appointmentService.prepare(courseRound, site, day);
            
            Principal coach = principalService.load(Principal.class, coachId);
            modelAndView.addObject("coach", coach);
            
            List<Site> sites = appointmentService.listSite(coach, day);
            if(sites.contains(site)) {
                boolean experience = (courseRound.getCourseType().isIndividual() && courseRound.getCourse().isExperience());
                
                // 5. find coach's CourseScheduler and restCalendar
                Map<String, CourseSchedule> scheduleMap = courseScheduleService.mapCourseSchedule(coach, day, experience);
                Map<String, CoachRestCalendar> restMap = coachRestCalendarService.mapCoachRestCalendar(coach, day, experience);
                
                appointmentPrepare.individualInit(restMap, scheduleMap);
                
                modelAndView.addAllObjects(appointmentPrepare.describeCoachAppoint());
            } else {
                appointmentPrepare.unavailable();
                
                modelAndView.addAllObjects(appointmentPrepare.describeUnavailable());
            }
            
            modelAndView.addObject("currentMinute", new Date().getMinutes());
            
            return modelAndView;
            
        } catch(BusinessException e) {
            
            logger.error("#appointmentByCoach() error", e);
            
            modelAndView.setViewName("course/course-coach-appointment-error");
            modelAndView.addObject("errorMessage", e.getMessage());
            
            return modelAndView;
            
        } catch(Exception e) {
            
            logger.error("#appointment() error", e);
            
            modelAndView.setViewName("course/course-coach-appointment-error");
            modelAndView.addObject("errorMessage", "");
            
            return modelAndView;
            
        } 
        
    } 
    
    /**
     * Validate if there is an avaliable coach here
     * 
     * @param user
     * @param appointment
     * @return
     */
    @RequestMapping(value = "/courses/{courseId}/appointment/check", method = RequestMethod.PATCH)
    @Router
    @WechatAuthority
    @MobileDetect
    @WechatJsConfig
    public @ResponseBody String onAppointmentCheck(@Secure User user, 
                                                   Appointment appointment,
                                                   @RequestParam(name = "scheduleId", required = false) Long scheduleId) {
        
        Result result = new Result();
        
        try {
            
            CourseRound courseRound = courseRoundService.loadCourseRound(appointment.getRoundId());
            Student student = userService.getStudent(user);
            Site site = persistentAccessor.site(appointment.getSiteId());
            
            // 1. do validate appointment
            if(scheduleId != null) {
                appointmentService.validateAppointment(student, courseRound, appointment, scheduleId);
            } else {
                appointmentService.validateAppointment(student, courseRound, appointment);
            }

            Date day = appointment.getDay();
            String hour = appointment.getHour();

            if(courseRound.getCourseType().isYear()) {
                appointmentService.validateYearCourse(student, courseRound, day, hour);
            }
            
            boolean experience = (courseRound.getCourseType().isIndividual() && courseRound.getCourse().isExperience());
            List<CoachPrepare> coaches = appointmentService.genereateYearCoach(site, day, hour, experience);
            result.data("coaches", JsonUtils.toJsonString(coaches));
            
            // 3. room data validate & generate 
            RoomPrepare roomPrepare = appointmentService.prepareRoom(courseRound, site, day, hour);
            
            result.data("appointment", JsonUtils.toJsonString(appointment))
                  .data("roomPrepare", JsonUtils.toJsonString(roomPrepare))
                  .success().message("获取数据成功");
            
        } catch(BusinessException e) {
            logger.error("onCheck() BusinessException error", e);
            result.fail().message(e.getMessage());
            
        }catch (Exception e) {
            logger.error("onCheck() error", e);
            result.fail().message("查询数据错误，请稍后再试");
            
        }
        
        return JsonUtils.toJsonString(result);
    }
    
    /**
     * 
     * @param user
     * @param scheduleId
     * @param appointment
     * @return
     */
    @RequestMapping(value = "/courses/{roundId}/appointment", method = RequestMethod.POST)
    @Router
    @WechatAuthority
    @MobileDetect
    @WechatJsConfig
    public @ResponseBody String onAppointment(@Secure User user,
                                              @RequestParam(name = "scheduleId", required = false) Long scheduleId,
                                              Appointment appointment) {
        
        Result result = new Result();
        
        try {
            
            // 1. load course round & student 
            CourseRound courseRound = courseRoundService.loadCourseRound(appointment.getRoundId());
            Student student = userService.getStudent(user);
            
            // 2. validate appointment
            if(scheduleId != null) {
                appointmentService.validateAppointment(student, courseRound, appointment, scheduleId);
            } else {
                appointmentService.validateAppointment(student, courseRound, appointment);
            }
            
            Principal coach = persistentAccessor.principal(appointment.getCoachId());
            appointmentService.validateCoach(coach, appointment.getDay(), appointment.getHour(), courseRound.getCourse().isExperience(), scheduleId);
            
            Date day = appointment.getDay();
            String hour = appointment.getHour();
            Site site = persistentAccessor.site(appointment.getSiteId());
            
            // 3. validate
            if(courseRound.getCourseType().isYear()) {
                appointmentService.validateYearCourse(student, courseRound, day, hour);
            }
            
            // 4. do appointment or change appoint
            RoomType roomType = RoomType.aliasOf(appointment.getAreaType());
            if (scheduleId == null) {
                appointmentService.appointmentSchedule(courseRound, site, coach, roomType, day, hour);
            } else {
                appointmentService.changeSchedule(scheduleId, coach, roomType, site, day, hour);
            }
            
            result.success().message("预约成功");
            
        } catch (BusinessException e) {
            
            logger.error("onAppointment() error", e);
            
            result.fail().message(e.getMessage());
            
        } catch (Exception e) {
            
            logger.error("onAppointment() error", e);
            
            result.fail().message("预约失败");
        }
        
        return JsonUtils.toJsonString(result);
    }
    
}
